Design Patterns מאיר סלע מהדורה ראשונה 2004 הדפסה 1 כל הזכויות שמורות

Similar documents
ASP.Net MVC + Entity Framework Code First.

מבוא לתכנות ב- JAVA תרגול 7

State Pattern מימוש מכונת מצבים (FSM) מבוא בעיה תמיכה ועדכונים עדכון מס' 48 מאי 2002

טכנולוגיית WPF מספקת למפתחים מודל תכנות מאוחד לחוויית בניית יישומיי

תצוגת LCD חיבור התצוגה לבקר. (Liquid Crystal Display) המערכת.

המבנה הגאומטרי של מידה

Patents Basics. Yehuda Binder. (For copies contact:

מכונת מצבים סופית תרגול מס' 4. Moshe Malka & Ben lee Volk

Practical Session No. 13 Amortized Analysis, Union/Find

ANNEXURE "E1-1" FORM OF IRREVOCABLE STANDBY LETTER OF CREDIT PERFORMANCE OF CONTRACT (WHERE PRICES ARE NOT LINKED TO AN ESCALATION FORMULA)

A R E Y O U R E A L L Y A W A K E?

A JEW WALKS INTO A BAR: JEWISH IDENTITY IN NOT SUCH JEWISH PLACES


FILED: NEW YORK COUNTY CLERK 07/16/2014 INDEX NO /2014 NYSCEF DOC. NO. 102 RECEIVED NYSCEF: 07/16/2014 EXHIBIT 5

FILED: NEW YORK COUNTY CLERK 07/16/2014 INDEX NO /2014 NYSCEF DOC. NO. 134 RECEIVED NYSCEF: 07/16/2014 EXHIBIT 37

אנגלית (MODULE E) בהצלחה!

קשירות.s,t V שני צמתים,G=(V,E) קלט: גרף מכוון מ- s t ל- t ; אחרת.0 אם יש מסלול מכוון פלט: הערה: הגרף נתון בייצוג של רשימות סמיכות.

מספר השאלון: Thinking Skills נספח: כישורי חשיבה )לפרק ראשון ושני( א נ ג ל י ת (MODULE F) ספרות )מילון הראפס אנגלי-אנגלי-ערבי(

Rules Game (through lesson 30) by Nancy Decker Preparation: 1. Each rule board is immediately followed by at least three cards containing examples of

אנגלית שאלון ז' ג רסה א' הוראות לנבחן בהצלחה! )4( ההנחיות בשאלון זה מנוסחות בלשון זכר ומכוונות לנבחנות ולנבחנים כאחד. (MODULE G)

מבחן באנגלית בהצלחה הצלחה!!! שם פרטי: שם משפחה: מס' תעודת זהות: תאריך: שם מרכז מנהל מרכז השכלה: תאריך בדיקת המבחן: כל הזכויות שמורות למשרד החינוך

נספח: כישורי חשיבה )לפרק ראשון ושני( אנגלית (MODULE F) ספרות או: מילון אנגלי-ערבי / ערבי-אנגלי או: מילון אנגלי-אנגלי-ערבי

Structural Vs. Nominal Typing

א נ ג ל י ת בהצלחה! ב. משרד החינוך בגרות לנבחנים אקסטרניים )מילון הראפס אנגלי-אנגלי-ערבי( השימוש במילון אחר טעון אישור הפיקוח על הוראת האנגלית.

אנגלית ספרות בהצלחה! /המשך מעבר לדף/ נספח: כישורי חשיבה )לפרק ראשון ושני( או: מילון אנגלי-ערבי / ערבי-אנגלי או: מילון אנגלי-אנגלי-ערבי

מטוסים נופלים, כורים מתפוצצים זיכרון אוטומטי מקטסטרופות לומדים בניינים קורסים,

הקיטסיגול הרבחה יעדמל בלושמה גוחה

פולימורפיזם. blog.csit.org.il מדעי המחשב

הטכנולוגיה בחינוך ד ר קובי גל אוניברסיטת בן גוריון בנגב

Reflection Session: Sustainability and Me

תכנית סטארט עמותת יכולות, בשיתוף משרד החינוך א נ ג ל י ת שאלון א' Corresponds with Module A (Without Access to Information from Spoken Texts) גרסה א'

Hebrew Ulpan HEB Young Judaea Year Course in Israel American Jewish University College Initiative

בהצלחה! (MODULE C) Hoffman, Y. (2014). The Universal English-Hebrew, Hebrew-English Dictionary

עץ תורשה מוגדר כך:שורש או שורש ושני בנים שכל אחד מהם עץ תורשה,כך שערך השורש גדול או שווה לסכום הנכדים(נכד-הוא רק בן של בן) נתון העץ הבא:

נספח: כישורי חשיבה )לפרק ראשון ושני( אנגלית (MODULE F) ספרות מילון אנגלי-אנגלי-עברי או מילון אנגלי-עברי-עברי-אנגלי

פיזיקה של נהיגה מדריך למורה

ניפוי שגיאות )Debug( מאת ישראל אברמוביץ

זו מערכת ישרת זוית )קרטזית( אשר בה יש לנו 2 צירים מאונכים זה לזה. באותו מישור ניתן להגדיר נקודה על ידי זוית ורדיוס וקטור

מדריך שימוש בדואר האלקטרוני

ãówh,é ËÓÉÔê ÌW W É Å t" Y w f É ËÓÉÑ É èw É f Ñ u ð NNM YóQ' ÌW W É Y ÉgO d óqk É w f ym Éd É u ð NNM ÌWNQMH uqo ð NNM ÌWNQMH

שאלון ו' הוראות לנבחן

נספח: כישורי חשיבה )לפרק ראשון ושני( אנגלית (MODULE D) ספרות או מילון אנגלי-עברי-עברי-אנגלי

אנגלית שאלון ז' (MODULE G) ג רסה א' הוראות לנבחן )מילון אנגלי-ערבי / ערבי-אנגלי )

שאלון ו' הוראות לנבחן

סמסטר אביב 2015, מועד ב' סמסטר קיץ 2015, מועד א' מתרגלים: אביב - נורית מושקוביץ', הלאל עאסי, אלירן וייס; קיץ מאיה דיאמנט

שאלון ד' הוראות לנבחן

נספח: כישורי חשיבה )לפרק ראשון ושני( אנגלית (MODULE D) ספרות מילון אנגלי-אנגלי-עברי או מילון אנגלי-עברי-עברי-אנגלי

מבוא למחשב בשפת Matlab

תרגול 8. Hash Tables

נספח: כישורי חשיבה )לפרק ראשון ושני( אנגלית (MODULE D) ספרות או מילון אנגלי-עברי-עברי-אנגלי

מבוא לתכנות - פיתוח משחקים ב Action Script 3.0

ב. משרד החינוך בגרות לנבחנים אקסטרניים א נ ג ל י ת (MODULE B) הוראות מיוחדות: )2( בתום הבחינה החזר את השאלון למשגיח. בהצלחה!

THINKING ABOUT REST THE ORIGIN OF SHABBOS

הבסיס כתיבת התכנית הראשונה שימוש במשתנים. הטיפוס הבסיסי object. הטיפוס הבסיסי string משפטי בקרה. שימוש ב- Enumerations. שימוש ב- Namespaces

חטיבת המינרלים החיוניים תתמקד בשוקי האגרו וחטיבת הפתרונות המיוחדים תשמש כחטיבה התעשייתית; כיל דשנים מיוחדים תשולב בחטיבת המינרלים החיוניים;

תכנית סטארט עמותת יכולות, בשיתוף משרד החינוך א נ ג ל י ת שאלון ב' Corresponds with Module B גרסה ב' הוראות לנבחן

שפת תיאור חומרה VHDL

נילי חמני

מספר השאלון: הצעת תשובות לשאלות בחינת הבגרות אנגלית (MODULE C) מילון אנגלי-אנגלי-עברי או מילון אנגלי-עברי-עברי-אנגלי قاموس إنجليزي - إنجليزي - عربي

Name Page 1 of 6. דף ט: This week s bechina starts at the two dots in the middle of

מיקוד באנגלית. Module D. New Program in English Literature. Option 1 שאלון אינטרני מספר שאלון אקסטרני מספר 414

מספר ת"ז: יש לסמן את התשובה הטובה ביותר בתשובון. לא יינתן ניקוד על סימון תשובה בטופס הבחינה או במחברת הבחינה.

A Long Line for a Shorter Wait at the Supermarket

ב. משרד החינוך בגרות לנבחנים אקסטרניים א נ ג ל י ת (MODULE B) הוראות מיוחדות: )2( בתום הבחינה החזר את השאלון למשגיח. בהצלחה!

סה"כ נקודות סה"כ 31 נקודות סה"כ 21 תוכן עניינים של פתרון המבחן. לולאת for )נתון אלגוריתם... מעקב, פלט

הקדמה פרופ' מוטי בן-ארי המחלקה להוראת המדעים מכון ויצמן למדע

דיאגרמה לתיאור Classes

3. class attr_accessor def end 9. end puts "

(MODULE E) ב ה צ ל ח ה!

Advisor Copy. Welcome the NCSYers to your session. Feel free to try a quick icebreaker to learn their names.

Computer Structure. Exercise #1 יש להגיש את התשובות הסופיות על גבי טופס זה.

תמ מהו הקדמה פרופ' מוטי בן-ארי. Peter תמ"ע = עצמים + מחלקות + ירושה. תמ"ע "לייט" לבין תמ"ע אמיתי. Turbo Pascal

מבוא לרשתות - תרגול מס' 11 Transparent Bridges

תוכנה 1 בשפת Java שיעור מספר 8: "ירושה נכונה" בית הספר למדעי המחשב אוניברסיטת תל אביב

מבוא לתכנות - פיתוח משחקים ב Action Script 3.0

byte code :(compiler) .class מהדר בתהליך עורך, סייר, קומפיילר,,JRE וכו'. למשל: אקליפס. נקודות חשובות: חסכון בהקצאת זיכרון.

NATIONAL COUNCIL OF YOUNG ISRAEL. Shavuot Nation JEWISH EDITION. Compiled by Gabi Weinberg Teen Program Director

פרק מחלקות ממשק המחלקה צריך גם לדעת להגדיר בתוכניתו מחלקות לפי הצורך. בפרק זה נלמד להגדיר מחלקה ולממשה על סמך ממשק נתון.

הצעת תשובות לשאלות בחינת הבגרות אנגלית

ל"תוכנה" שכותבים, כמו פונקציה זו, קוראים "קוד"

Theories of Justice

תכנות מונחה עצמים סביבת Java

Depth-First Search DFS

T H E S U N F L O W E R L I M I T S T O F O R G I V E N E S S

שאלות חזרה לקראת מבחן מפמ"ר אינטרנט וסייבר

2007 Zindell Technologies, Ltd.

פרק מחלקות ממשק המחלקה צריך גם לדעת להגדיר בתוכניתו מחלקות לפי הצורך. בפרק זה נלמד להגדיר מחלקה ולממשה על סמך ממשק נתון.

המחלקה למדעי המחשב, אוניברסיטת בן גוריון מבני נתונים, סמסטר אביב 2102 עבודת בית מספר - 2 מעשית

Genetic Tests for Partners of CF patients

פקס בחיבור המכשיר המשולב לפקס יש לעבור על השלבים הבאים: - חבר את כבל הטלפון לחיבור ה- LINE בגב המדפסת ואת צידו השני לשקע הטלפון בקיר.

סטטיסטיקה בתכנית "מוסמך" ש"ת, ש 3 "ס.

Homework 10. Theoretical Analysis of Service Stations in Steady State. Priority Queues.

תרגול 11 תור עץ חיפוש בינארי

DNS פרק 4 ג' ברק גונן מבוסס על ספר הלימוד "רשתות מחשבים" עומר רוזנבוים 1

Software Life-Cycle Models מודלים של מחזור חיי תוכנה - 1

מבו א ל- VHDL אז מה י ה י ה לנ ו ה י ו ם... מהי שפת הגדרת חומרה ולשם מה דרושה תיאור, סימולציה, סינתזה

תורשכ ירפס לכ ץבוק " ב י קלח יללכ רעש

מבחן מועד ב' אנא קיראו היטב את ההראות שלהלן:

גירסה C++ Tutorial Nir Adar עמוד 1

Transcription:

Design Patterns 4 Design Patterns מאיר סלע מהדורה ראשונה 2004 הדפסה 1 כל הזכויות שמורות מרכז ההדרכה עיטם 2000 אתר אינטרנט: www.mh2000.co.il דואר אלקטרוני: info@mh2000.co.il אין להעתיק, לשכפל או לצלם ספר זה או קטעים ממנו, בשום צורה ובשום אמצעי אלקטרוני, אופטי או מכני לכל מטרה שהיא, ללא אישור בכתב מההוצאה לאור.

פרק : 1 מבוא 13. 1 מבוא נושאי הפרק: Patterns - Design הגדרה, מאפיינים, יישום מדדים בפיתוח תוכנה - וכיצד Design Patterns מסייעים בהשגתם Reference - מקורות ספרות

Design Patterns 14 Design Patterns design patterns הוא תחום במסגרת הכללית של תיכון מונחה עצמים Design),(Object Oriented הכולל טכניקות תיכון ותכנות מתקדמות. המקור התיעודי העיקרי ל- patterns הוא הספר[ Gamma95 ]. לספר ארבעה מחברים, ולכן הם מכונים לפעמים בקיצור גם.Gang Of Four - GOF מחברי הספר עצמם מעידים שמקור ההשראה העיקרי עבורם היה ספרו של כריסטופר אלכסנדר [Alexander79] שעסק בתכנון וארכיטקטורה אורבניים. בספר זה נסקור את ה- patterns התקניים והמוכרים בתעשיית התוכנה. לכל pattern ניתן שם מזהה ותיאור בשלושה חלקים: תיאור הקשר (context) הבעיה תוך התבוננות בדוגמא תיאור הפתרון בצורת תרשים תיכון,UML כמו גם קוד דוגמא ב-.C++/Java/C# הכללת הפתרון למקרה הכללי תוך ציון קשרים עם patterns אחרים, וריאציות שונות של הפתרון, השלכות, ושימושים לדוגמא. כפי שנאמר, המקור העיקרי ל- patterns הוא הספר.[Gamma95] עקב גילו, שפת התרשימים שבו אינה עדכנית (לא,(UML מערכות העצמים ודוגמאות הקוד שבו מיושנות. ספר זה עושה שימוש בתרשימי,UML כולל עדכונים רבים, תוספות ודוגמאות ממערכות עצמים מודרניות. - Design Pattern מה זה? קיימות מספר הגדרות ל-,design pattern אולם הדרך הבהירה ביותר להבנתו היא באמצעות מאפייניו. pattern מצוי : מספק פתרון לבעיה כללית החוזרת ומופיעה בצורות שונות יכול לכלול מספר וריאציות של הפתרון (בהתאם לוריאציות של הבעיה ולשיקולי תיכנון נוספים) נבדק ונוסה כבר פעמים רבות (בהצלחה) בד"כ מתייחס לתחושת dejavu של מפתחים מנוסים בהקשר הנתון לרוב ממומש ע"י שיתוף פעולה בין מספר מחלקות/עצמים לרוב הוא אינו המצאה או גילוי של אדם מסויים

פרק : 1 מבוא 15 הגדרה של מחברי הספר :GOF Design Patterns - Descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context. הגדרה של אלכסנדר בספרו [Alexander79] : Each pattern is a three-part rule, which expresses a relation between a certain context, a problem, and a solution. מטרות בלימוד ה- patterns המטרות העיקריות בלימוד ה- patterns שבספר זה הן: הכרה וזיהוי של תבניות בעיות ופתרונן - בכדי לזהות pattern ראשית יש להכירו, ולהפנים את המבנה שלו, כמו גם את שמו. לא תמיד קל לזהות,patterns אולם הזמן והתרגול מסייעים בכך. רכישת שפה "גבוהה" לביטוי ולתיעוד התבניות הנ"ל - לאחר תהליך ההפנמה של ה- patterns ניתן לקטוף פירות נוספים: קיבלנו שפת patterns שניתן להשתמש בה במסגרת תיכון, ייעוץ, שיחה, תיעוד ולימוד. לדוגמא, במקום לכתוב בתיעוד מחלקה מסויימת בתכנית שהגדרת את ה- constructor כ- protected בכדי למנוע ייצור מפורש של עצמים מהמחלקה, אך סיפקת פונקציה סטטית לקבלת המופע היחיד שלו, אתה יכול להשתמש במילה אחת השקולה לתיאור הנ"ל:.Singleton לימוד מניסיונם של אחרים בנושאי תיכון ותכנות - ה- patterns הם למעשה מאגר של הידוע עד כה בנושאי תכנות מונחה עצמים. מאגר זה נאסף ונבדק במשך שנים על סמך נסיונם של רבים וטובים, והצגת הידע הזה כקטלוג של טכניקות תכנות מקצרת את תהליך הלימוד של ה"תורה שבעל-פה" עבור מהנדסי תוכנה פחות מנוסים ב-.OO תוצרי לוואי: לימוד ותרגול UML כשפה לתיאור מודלי תוכנה - מרבית הדיונים שבספר זה מתבצעים ברמת התיכון ב-,UML כאשר פה ושם מובאות דוגמאות קוד. אחת התוצאות של לימוד ה- patterns היא הכרת UML בצורה מחודדת ומעמיקה. התנסות בתיכון ובמעבר מתיכון למימוש - שיקולי התיכון שמלווים חלק גדול מה- patterns מספקים התנסות עשירה בבחינת השפעתם במעבר למימוש.

Design Patterns 16 הכרה והתנסות עם מנגנונים מתקדמים ב- :OOP פולימורפיזם ופולימורפיזם טהור,.RTTI,templates / Generics חשיפה למערכות עצמים מתקדמות - בספר מובאות דוגמאות בשפות ++C, #C,Java ובמערכות.MFC,.NET, Java, COM ה- patterns עוזרים להבין בקלות ובמהירות מערכות וספריות מורכבות מאוד. מדדים בפיתוח תוכנה המטרה המשותפת לכל ה- patterns שבספר זה היא פיתוח תוכנה "טובה" יותר. השאלה הראשונה שעולה מכאן היא "מהי תוכנה טובה יותר?" - לשם כך קיימים מספר מדדים מקובלים להערכת תוכנה: גמישות - היכולת לבצע שינויים בתוכנה ללא מאמץ רב. הרחבה - היכולת להרחיב את התוכנה (בדרך כלל פרוייקטי תוכנה מתרחבים עם הזמן). יעילות - ניצול משאבי מקום וזמן שצורכים מרכיבי המערכת. בהירות - היכולת ללמוד את המערכת ללא מאמץ רב ובזמן קצר. ניידות - יכולת הסבה של התוכנה לספריות, מערכות ומחשבים שונים. במילים אחרות, תוכנה היא טובה יותר ככל שהיא יעילה יותר, גמישה יותר בפני ביצוע שינויים, מאפשרת תוספות בצורה קלה ופשוטה יותר וככל שניתן להסב אותה לסביבות שונות ביתר קלות. מדד אחר שמקובל להגדיר באופן כללי הוא פשטות, והוא כולל בתוכו את כל המדדים האחרים: ככל שהמערכת פשוטה וטבעית יותר, היא מובנת יותר, נוחה יותר להרחבה ולשינוי ולכן טובה יותר. באנגלית קיים קיצור KIS לביטוי Simple"."Keep It מדדים פרטניים יותר השאלה: כיצד בונים תוכנה שתענה לדרישות הנ"ל - יעילה, בעלת יכולת גמישות, יכולת הרחבה ויכולת נייוד טובות יותר?בכדי לענות לשאלה, עלינו לפרוט את המדדים הנ"ל למדדי משנה: מודולריות - מערכת מודולרית היא מערכת המחולקת למרכיבי תוכנה, באופן היררכי, כך שכל נושא בה מטופל ע"י מרכיב אחד. מדד זה נקבע בשלבי התיכון המוקדמים של המערכת: בתהליך ה- decomposition מכריעים כיצד לפרק את המורכבות של המערכת לכדי מודולים, כך שהצימוד (להלן) ביניהם יהיה קטן ככל האפשר. כמו כן, תכנון מודולרי של המערכת מאפשר שימוש חוזר (Reusability) במרכיביה ביתר קלות.

פרק : 1 מבוא 17 צימוד (Coupling) - Decoupling / אלו הם שני הפכים: בתכנון מערכת אנו מנסים להשיג decoupling מקסימלי בין רכיבי תוכנה שונים. בדרך כלל, ככל שהצימוד קטן יותר קל יותר לבצע שינויים והרחבות. פולימורפיזם הוא מנגנון יעיל מאוד לפיתוח רכיבי תוכנה עם צימוד מינימלי. כמעט כל ה- patterns מקטינים את הצימוד שבין מרכיבים שונים הן ע"י שמוש בפולימורפיזם והן ע"י שימוש בהפנייה.(Forwarding) לדוגמא, ה- Command (עמוד 185) מקטין את הצימוד שבין יוזם הפקודה לבין העצם המקבל ומבצע אותה. יתר על כן, היוזם עצמו אינו מכיר ולכן גם לא תלוי בסוגי ה- Commands הקיימים במערכת, בזכות המבנה הפולימורפי שלהם. מה שאומר ששינוי כלשהו במבצעי הפקודות, או הוספת Commands חדשים למערכת, אינם מצריכים הידור מחודש שלו. ה- Proxy (עמוד 105) מקטין את הצימוד שבין קוד הלקוח לבין עצם נתון ע"י חציצה בין השניים וביצוע הפנייה (Forwarding) של קריאות הלקוח לעצם המטרה. ניצול מקום טוב יותר - בתכנון נכון יותר של המערכת ניתן לצרוך פחות מקום בזכרון התכנית, הן זה שבשימוש ע"י הקוד והן זה שבשימוש ע"י הנתונים. ה- Proxy (עמוד 105) וה- Flyweight (עמוד 162) מספקים ניצול טוב יותר של זכרון התכנית ע"י שימוש בשיתוף. ניצול זמן טוב יותר - הדרך שבה מבוצעות פעולות במערכת יכולה להשפיע בסדרי גודל על ביצועי הזמן שלה. לדוגמא, שימוש בפולימורפיזם בתכנון OO הוא יעיל יותר משימוש במשפטי תנאי כגון: - switch-case קריאה לפונקציה וירטואלית מתבצעת ב- (1)O, בעוד ש- switch-case בוחר את הכניסה המתאימה ב-,O(n) כאשר n הוא מספר ה- case -ים שבמשפט. מגוון של patterns התנהגותיים מייעלים את זמן הביצוע של משימות באופן הנ"ל: Command (עמוד,(185 State (עמוד,(199 Visitor (עמוד,(232 Prototype (עמוד,(60 Prototype-based Factory (עמוד (64 פרמטר זמן נוסף הוא משך הפיתוח של המערכת: גם כאן, ניתן בבירור לומר שתוכנה מודולרית ופשוטה יותר תהיה בעלת משך פיתוח קצר יותר מזה של מערכת פחות מודולרית. תלות (Dependency) / אי-תלות - עפ"י דרישות היישום, אנו משתדלים להבין אילו תלויות עלולות להכביד על ביצוע שינויים והרחבות במערכת כבר בשלב התכנון. בתכנון שלוקח בחשבון שינויים עתידיים Change ) ( Design for מקטינים למינימום את התלויות במרכיבים העשויים להשתנות. לדוגמא, אם אלגוריתם המוגדר ע"י פונקציה במחלקת תוכנה כלשהי עשוי להשתנות, ואם מעבר לכך האפשרות להחליפו באלגוריתמים אחרים באופן דינמי עשויה לקדם את הפיתוח, רצוי לשקול שימוש ב- Strategy (עמוד 245). יכולות דינמיות - פה אנו מדברים על האפשרות לבצע שינויים ותוספות בזמן ריצה. לדוגמא, תכנון מסויים של יישום הכולל תפריט עשוי לטפל בכל האפשרויות הקיימות בתפריט בזמן קומפילציה. אולם, האם ניתן להוסיף פריטים או להסיר פריטים מהתפריט בזמן ריצה? יכולת דינמית כזו חוסכת את הצורך בקידוד ובהידור מחודש בכל שינוי בתפריט. ה-

Design Patterns 18 Command (עמוד 185) מתאר דרך יעילה למענה לדרישה זו. באופן דומה, State (עמוד 199) מספק יכולת דינמית בקביעת מצב העצם, כמו גם להוסיף ולבטל מצבים בזמן ריצה. Strategy (עמוד 245) מספק יכולת דינמית בקביעת סוג האלגוריתם שיופעל בקריאה לפונקציה מסויימת של עצם נתון. מיפוי דרישות ספציפיות ל- patterns design patterns מיועדים למתן מענה לדרישות מסויימות, בהקשר נתון. בסעיף זה נראה מיפוי של דרישות ספציפיות ל- patterns המתאימים: אי-תלות באופן הייצוג והמימוש של העצם - לפי דרישה זו קוד הלקוח לא צריך להיות מודע לפרטי הקוד של המחלקה. Adapter (156), Bridge (166), Facade (133), Proxy (105), Memento (224) אי-תלות באופן יצירת העצם - קוד הלקוח לא צריך להיות מודע לאופן יצירת העצמים. במקרים רבים, דרישה זו היא תוצאה ישירה של הדרישה הקודמת. Prototype-based Factory (64), Factory Method (73), Abstract Factory (72), Builder (77), Proxy (105) אי-תלות באלגוריתם - נדרשת יכולת הרחבה או שינוי של אלגוריתם מסויים בעצם במהלך הפיתוח. כמו כן לעיתים אלגוריתם מסויים של העצם צריך להקבע באופן דינמי. Strategy (245), Template Method (240), Visitor (232), Iterator (209) אי-תלות בין אופן ייצוג נתונים לבין הצגתם - הפרדה בין מודל הנתונים של היישום לבין ההצגה הויזואלית שלהם מפשטת את תהליך הפיתוח. MVC (180)

פרק : 1 מבוא 19 Reference [Alexander79] [Booch97] [Booch99] [Gamma95] [Meyers98] [Meyers96] [MH-C++02] [Strous94] The Timeless Way of Building, Christopher Alexander, Oxford University Press, 1979. Object Oriented Analysis and Design with Applications, Grady Booch,Benjamin Cummings, 1997 הספר כולל טכניקות תיכון מונחה עצמים כלליות, ובכך הוא משמש כמשלים ל- [1994.[Gamma The Unified Modeling Language User Guide, Grady Booch, James Rumbaugh, Ivar Jacobson, Addison Wesley, 1999 ספר מקיף ללימוד,UML מאת האבות היוצרים של תקן זה. החומר מובא בצורה שוטפת וברורה, עם תרשימים רבים ודוגמאות ממערכות עדכניות. Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. (GOF), Addison Wesley 1995. המקור הרשמי לנושאי.Design Patterns התוכן ברור ומובא בצורה נוחה לקריאה. עקב גילו של הספר, מוסכמות הסימונים והדוגמאות המובאות בו לא עדכניות. Effective C++, 2 nd Edition, Scott Meyers, Addison Wesley, 1998 More Effective C++, Scott Meyers, Addison Wesley, 1996 ++C - מדריך מקצועי, מאיר סלע, מרכז ההדרכה עיטם (2000), שנת 2002 ספר מקיף ומקצועי בנושא שפת ++C בפרט ותיכון מונחה עצמים בכלל. ספר זה שבהוצאתנו כולל תיאור של כלל מרכיבי שפת ++C העדכנית. The design and evolution of C++, Bjarne Stroustrup, Addison Wesley, 1994 למרות גילו של הספר, הוא עדיין משמש כאחד המקורות המרהיבים להבנת מבנה שפת ++C. המחבר, שהוא גם יוצר השפה, מביא את השיקולים השונים שעמדו בבחירת מנגנונים מסוימים לשפה ובדחיית אחרים, ובכך מרחיב את הבנת הקורא ב- ++C בפרט ובתוכנה בכלל.

Design Patterns 20 [Strous97] [Vlissid98] The C++ Programming Language, 3 rd Edition, Bjarne Stroustrup, Addison Wesley, 1997 ספר התנ"ך לשפת התכנות ++C, מאת יוצר השפה. הספר מקיף מאוד ומעמיק מאוד באופן בלתי מתפשר, לעיתים אף על חשבון הדידקטיות. התוכן מצטיין בניסוח מדויק וזהיר. Pattern Hatching, Design Patterns Applied, John Vlissides, Addison Wesley 1998.

פרק : 3 patterns בייצור עצמים 51. 3 patterns בייצור עצמים ה- patterns שבפרק זה עוסקים בייצור של עצמים, עבור מקרים בהם משימה זו אינה טריוויאלית, או שצריכה להתבצע תחת מגבלות מסוימות. Singleton - הגבלת מספר העצמים המיוצרים ממחלקה מסוימת בתכנית ל- 1, ומתן גישה גלובלית לעצם היחיד. Prototype - ייצור פולימורפי של עצם עפ"י אבטיפוס (prototype) נתון ע"י שיבוט. Factory - Prototype-based ייצור פולימורפי של עצם עפ"י מפתח, תוך שימוש ב-.Prototype כמו כן נסקור בקצרה מספר patterns סטנדרטיים נוספים: Factory - Abstract ממשק לייצור משפחות עצמים ללא ציון הטיפוס הקונקרטי שלהם. Method - Factory פונקציה לייצור עצם המוגדרת במחלקת הבסיס, כשהמחלקות הנגזרות מחליטות איזה עצם בדיוק לייצר. Builder - הפרדת הייצור של עצם מורכב מהייצוג שלו, כך שאותו תהליך ייצור יאפשר לבנות ייצוגים שונים של העצם. כסיכום לסעיף זה נראה כיצד ניתן לממש Serialization במערכת מונחית עצמים תוך שימוש ב- patterns הנ"ל.

Design Patterns 52 Singleton בעיה נתונה מחלקת מערכת System המספקת שירותי מערכת שונים: שירותי שעון, שירותי ניהול זיכרון, טעינת ספריות דינמיות,(DLL) וכן קבלת מידע על תכונות המערכת הנוכחית: System -properties +System() +time_service() +mem_service() +load_library() +get_property() // system.h #include <string> #include <map> using namespace std; קוד המחלקה: class System public: System() properties["os"]="windows"; properties["lang"]="heb"; void time_service() /*...*/ void mem_service() /*...*/ void load_library (string lib) /*...*/ string get_property(string name) const return properties[name]; private: map<string, string> properties; ; דרישות אנו מעונינים שמ- System יהיה מופע אחד לכל היותר בתכנית. כמו כן, אנחנו מעונינים לספק גישה גלובלית לעצם היחיד של המחלקה. יש לשים לב לדרישות מהפתרון: לכל היותר עצם אחד מהמחלקה System יהיה בתכנית. משמעות הדבר, שייתכן מצב בו לא יהיה גם עצם אחד - במידה ולא נעשה שימוש בו בריצת התכנית.

פרק : 3 patterns בייצור עצמים 53 הפתרון צריך להיות כפוי על כל קוד התכנית - כלומר, יש להבטיח שלא ייווצר יותר מעצם יחיד בתכנית אף לא כתוצאה משגיאה של מתכנת/ת. יש לאפשר גישה לעצם היחיד מכל מקום בתכנית, כלומר, הגישה לעצם צריכה להיות גלובלית. פתרון שגוי system.h: #include <string> #include <map> using namespace std; פתרון 1: הגדרת העצם כגלובלי. נכריז על העצם בקובץ ה- h.: class System... ; extern System g_system; // declaration of global system object system.cpp: #include system.h System g_system; ונגדיר אותו בקובץ המימוש: // definition of global system object קוד המשתמש יפעיל את השירותים של,g_system למשל כך: מה לא בסדר בפתרון זה? g_system.load_library( my_lib.dll ); אין מניעה של אפשרות ייצור עצמים נוספים מהמחלקה. עמידות הפתרון נתונה לחסדיו של קוד המשתמש - היא אינה נכפית עליו ע"י המהדר. בדרישות הבעיה צויין שיש לאפשר ייצור עצם אחד לכל היותר, ולא לייצר אותו אם לא נעשה בו שימוש. כאן אנו מקצים את העצם תמיד. "זיהום" מרחב השמות הגלובלי - המשתנה g_system נוסף למרחב השמות הגלובלי. במערכות גדולות מאוד, קיימת הסתברות להתנגשות עם שמות זהים במרחב הגלובלי, ונדרשות מוסכמות במתן שמות בכדי למנוע זאת. לדוגמא, נהוג להוסיף לשמות במרחב השמות הגלובלי 3 אותיות המציינות את שם תת-המערכת לה משתייך השם.

Design Patterns 54 בעיה חמורה במיוחד עלולה לצוץ באיתחול המערכת: נניח שבמערכת מספר עצמי Singleton כנ"ל - System - DB, Logger, Clock, ובאיתחול שלהם (ב- (constructor הם תלויים הדדית עפ"י הגרף הבא: Clock System Logger DB לדוגמא, חץ מ- DB ל- Logger מציין שב- constructor של המחלקה DB עושים שימוש בעצם ה-.Logger לכן על העצם Logger להיווצר לפני יצירת.DB אולם, מכיוון שב C/C++ לא מוגדר סדר על יצירת העצמים הגלובליים (המוגדרים בקבצים שונים), מתעוררת פה בעיה איתחול חמורה. הדרכים להתמודדות עם בעיה זו הן להגדיר את כל העצמים הגלובליים בקובץ מימוש אחד (ואז סדר היצירה הוא עפ"י סדר הגדרתם), או לבצע 2 פאזות של איתחול initialization).(double phase system.h: #include <string> #include <map> using namespace std; פתרון 2: הגדרת כל ה- members כסטטיים: class System public: static void time_service() /*...*/ static void mem_service() /*...*/ static void load_library (string lib) /*...*/ static string get_property(string name) const return properties[name]; private:

פרק : 3 patterns בייצור עצמים 55 ; static map<string, string> properties; system.cpp: #include system.h ובקובץ המימוש יש להגדיר את המשתנים הסטטיים של המחלקה: map<string, string> System::properties; בפתרון זה - לעומת הקודם - מובטח לנו שלא יהיה ניתן לייצר יותר מהעתק אחד של ה- members של המחלקה (ייתכן ומספר מתכנתים ייצרו מספר מופעים ל-,System אך הם עדיין יחלקו את אותם,members עקב היותם סטטיים). כמו כן, מרחב השמות הגלובלי לא מזוהם, מכיוון שאין צורך בייצור עצם מהמחלקה - השירותים שבה מופעלים בהקשר למחלקה, למשל כך: מה לא תקין בפתרון זה? System::load_library( my_lib.dll ); כמו בפתרון הקודם, גם כאן ה- members מוקצים תמיד, גם אם לא נעשה בהם כל שימוש בריצת היישום. כמו בפתרון הקודם, סדר היצירה של עצמים סטטיים, כמו גלובליים, לא מוגדר ולכן הבעיה שרירה וקיימת. פתרון המחלקה System תוגדר כ-.Singleton היא תכלול: פונקציה סטטית בשם instance() המספקת עצם מהמחלקה. עצם זה יוגדר כסטטי בפונקציה. ה- constructor יוגדר כ- protected בכדי למנוע ייצור עצמים ע"י קוד חיצוני. מאותה סיבה גם ה- copy constructor יוכרז כ- protected (הוא לא מוגדר כ- private בכדי לאפשר ירושה מה-.(Singleton

Design Patterns 56 System -properties -System() +time_service() +mem_service() +load_library() +get_property() +instance() // system.h #include <string> #include <map> קוד המחלקה ב- ++C: using namespace std; class System public: void time_service() /*...*/ void mem_service() /*...*/ void load_library (string lib) /*...*/ string get_property(string name) const return properties[name]; // global point of access to the sole instance static System& instance() static System object; return object; protected: System() properties["os"]="windows"; properties["lang"]="heb"; System(const System&); private: map<string, string> properties; ; יש לשים לב שה- copy constructor כלל אינו נדרש להגדרה - אולם מכיוון שמסופק כזה במחדל, מכריזים עליו כ - protected ולא ממשים אותו (היינו יכולים לממש אותו כפונקציה ריקה, וגם אז הוא גורם לשגיאת הידור בנסיון של קוד חיצוני למחלקה לעשות בו שימוש. היתרון שבאי מימושו הוא שכעת תתקבל שגיאת הידור גם בנסיון של קוד המחלקה עצמה, או מחלקות צאצאות, לנסות לייצר העתקים של העצם!) קוד משתמש לדוגמא: #include <iostream> #include "system.h" using namespace std;

פרק : 3 patterns בייצור עצמים 57 void main() System::instance().time_service(); System::instance().mem_service(); System::instance().load_library("mylib.dll"); cout << "Operating system: " << System::instance().get_property("os") << endl; cout << "Language: " << System::instance().get_property("lang") <<endl; System s1; // error: private constructor System s2(system::instance());// error: private copy constructor הכללה מגדירים מחלקה כ- Singleton כאשר מעוניינים לספק מופע אחד שלה, לכל היותר, בתכנית. הגישה למופע זה היא גלובלית עבור כל מרכיבי התכנית: Singleton #Singleton() +operation1() +operation2() +operation3() +instance() פונקציה סטטית מספקת reference לעצם היחיד המוגדר בתוכה כסטטי ה- constructors מוגדרים כ- protected יתרונות: ה- Singleton מונע "זיהום" של מרחב השמות בכך שעצם המחלקה אינו מוגדר במרחב השמות הגלובלי. בכך למעשה גם מסופקת גישה תקנית לעצמים של.instance() המשתמש תמיד ניגש לעצמים ע"י הפונקציה :Singletons Singleton זמין תמיד למשתמש בו: בניגוד לעצמים גלובליים שעבורם סדר הבנייה אינו מוגדר בתכנית ++C - ולכן אינם יכולים להתייחס אחד לשני בשלבי ה- constructors שלהם - ה- Singletons נוצרים עפ"י סדר השימוש בהם, ולכן לא חלה עליהם מגבלת שימוש כלשהי.

Design Patterns 58 חסרונות: קשה לעשות שימוש ב- Singleton בהיררכיות ירושה. כלומר, יש קושי עבור מחלקת ה- Singleton לרשת ממחלקה אחרת, או לרשת ממחלקת ה-.Singleton ה- Singleton הוא יחיד פר יישום, הואיל והוא מוגדר כסטטי בקטע הנתונים.Thread פר Singleton לא ניתן להגדיר בטכניקה זו.(Data Segment) וריאציות: קיימת שיטה נוספת להגדרת :Singleton ניתן להגדיר מצביע לעצם היחיד כסטטי במחלקה member),(static ולייצר אותו בפעם הראשונה שנקראת הפונקציה הסטטית.instance() שיטה זו היא מעט יותר מסורבלת ובעלת חסרון משמעותי: ה- [Vlissid98] אינו נקרא בסוף התכנית (ראה/י דיון ב- Singleton של ה- destructor בסעיף Singleton.( To kill a בשפות כגון Java/C# הבעיה לא קיימת בזכות ה-.Garbage Collector מימוש Singleton ב- Java ב- Java לא קיים מנגנון של משתנים סטטיים-מקומיים. לכן אפשרות המימוש היחידה היא זו של.static-member דוגמא למימוש Singleton בשיטת ה- :static member class Singleton private static Singleton m_instance; // the singleton object public static Singleton instance() // static access method if (m_instance == null) m_instance = new Singleton(); return m_instance; protected Singleton()... // protected constructor // rest of the class Safety :Thread במערכת מרובת,Threads עלול להיווצר מצב שבו שני Threads (או יותר) יבצעו את שורת היצירה של ה- Singleton. זה יכול לקרות כאשר מבוצעת החלפת Threads מיד לאחר שורת הבדיקה if (m_instance == null) <<context switch>> m_instance = new Singleton(); ניתן למנוע בעיה זו ע"י הוספת מנגנון סינכרון - ניתן להגדיר את קטע יצירת העצם כ- :synchronized public static Singleton instance()

פרק : 3 patterns בייצור עצמים 59 synchronized(singleton.class) // protect critical code if (m_instance == null) m_instance = new Singleton(); return m_instance; יש לשים לב לכך שההגנה מבוצעת בהקשר המחלקה (Singleton.class) מכיוון שהפונקציה היא סטטית, והיא נקראת בהקשר זה. הערות: אם מחלקת ה- Singleton מממשת את java.io.serializable הרי שניתן לייצר יותר מעצם אחד ממנה ע"י ביצוע חוזר של.deserialization מכיוון שה- constructor מוגדר כ-,protected יכולות מחלקות צאצאות של Singleton או מחלקות המשתייכות לאותו ה- package לעקוף את מגבלת הייצור שלו. אם רוצים למנוע זאת, ניתן להגדיר את ה- constructor כ-,private אולם בכך גם תמנע אפשרות ירושה מה-.Singleton מימוש Singleton ב- #C הקוד ב- #C דומה לזה של,Java עם מעט הבדל במנגנון הסינכרון ובמודל השיקוף: class Singleton protected static Singleton m_instance; // the singleton object public static Singleton instance() // static access method lock(typeof(singleton)) // protect critical code if (m_instance == null) m_instance = new Singleton(); return m_instance; protected Singleton() /*...*/ // protected constructor גם כאן אנו מבצעים הגנה מפני גישה של יותר מ- Thread אחד ע"י מנגנון הסינכרון,lock שמגן על טיפוס המחלקה של Singleton (מסוג (Type המתקבל מ-.typeof(Singleton)

Design Patterns 60 Prototype בעיה נתונה היררכית מחלקות הודעות: «interface» Message Fax -m_number -m_image Mail -m_address -m_text Memo -m_text הבעיה: כיצד לייצר עצם כהעתק של עצם הנתון באופן פולימורפי. למשל, הפונקציה הבאה מקבלת כפרמטר מצביע פולימורפי, והיא מעונינת לייצר העתק שלו: void f(message *pm) Message *new_msg =??? //... מכיוון שלא ברור מהו הטיפוס המדויק של העצם המוצבע ע"י pm אין דרך פשוטה לייצר עצם דומה לו.

פרק : 3 patterns בייצור עצמים 61 פתרון נוסיף פונקציה וירטואלית בשם clone() להיררכיה של ההודעות שמחזירה העתק של העצם: «interface» Message Fax -m_number -m_image Mail -m_address -m_text Memo -m_text הפונקציה clone() היא למעשה מעין copy constructor וירטואלי. היא תוגדר כוירטואלית טהורה במחלקת הבסיס כך: class Message public: virtual ~Message() virtual Message * clone() const = 0; virtual void set(const string s1, const string s2) = 0; virtual void print() const = 0; ; יש לשים לב שהפונקציה מחזירה מצביע פולימורפי ל-.Message כך, למשל, תדרוס המחלקה Fax את הפונקציה: class Fax : public Message public: Fax() : m_number(0) Fax(const string num, const string image) : m_number(atol(num.c_str())), m_image(image) virtual Message* clone() const return new Fax(*this); virtual void set(const string num, const string image) m_number=atol(num.c_str()); m_image=image; virtual void print() const cout << " * Fax: num=" << m_number << " Image=" << m_image << "\n"; private: long m_number; string m_image; ; וכך תמומש הפונקציה ()f שלעיל:

Design Patterns 62 void f(message *pm) Message *new_msg = pm->clone(); //... הכללה Prototype (הנקרא גם Constructor ( Virtual הוא פתרון למצב בו צריך לייצר עצם בזמן ריצה, שרק טיפוסו הפולימורפי ידוע בזמן הידור. תרשים כללי: Client +operation() prototype Prototype p = prototype->clone() ConcretePrototype1 ConcretePrototype2 return copy of self return copy of self יתרונות: מודולריות: קוד הלקוח אינו תלוי בטיפוסים המסוימים הנגזרים ממחלקת הבסיס - הפונקציה clone() תחזיר תמיד העתק של העצם הנתון. יעילות: העתקת העצם מבוצעת באופן מיידי, בניגוד לשאילתת טיפוס הצורכת זמן ומקום. חסרונות: לא ניתן להעביר פרמטרים מטיפוסים שונים עפ"י העצם המיוצר לפונקציה,clone() הואיל והממשק שלה חייב להיות אחיד (פולימורפי). כתחליף, יש לספק פונקציה לקביעת מצב העצם (set()) שתיקרא מייד לאחר העתקת העצם. יש קושי בהחלטה כיצד להעתיק את העצם: העתקה רדודה Copy) (Shallow או העתקה עמוקה Copy).(Deep אם העצם כולל מצביעים, קיים קושי בהחלטה האם לספק עצם החולק את המצביעים, או לספק עצם עם מידע כולל חדש.

פרק : 3 patterns בייצור עצמים 63 בדרך כלל מופעל ה- copy constructor בפונקציה,clone() והוא זה ש"מחליט" כיצד להעתיק את העצם, אולם ניתן להפעיל גם constructor מחדל, למשל. וריאציות : Stroustrap מציין בספרו [Strous97] במסגרת ה- Relaxation Rules את האפשרות (בהתאם למימוש הקומפיילר) להחזיר במחלקה הנגזרת מצביע לטיפוס שלה, כלומר: class Base... virtual Base * clone() const = 0; ; class Derived : public Base... virtual Derived * clone() const return new Derived(...); ; זה יאפשר שימוש לא פולימורפי בפונקציה Derived::clone() גם ללא צורך ב-.casting לא הרבה קומפיילרים תומכים באפשרות זו כיום. שימושים ידועים: Java כוללת במחלקת הבסיס שלה,,Object את השירות clone() המחזיר מצביע מסוג Object לעצם הנוכחי, תוך שימוש במנגנון השיקוף. בהיות Object בסיס לכל המחלקות, בכך מסופק ממשק Prototype לכל עצמי.Java יש לשים לב שהמחלקות הנגזרות אינן צריכות לדרוס את הפונקציה clone() - גירסת הבסיס Object מבצעת את כל העבודה תוך שימוש במודל השיקוף (Reflection.Model) מחלקת Java צריכה לממש את הממשק Cloneable בכדי לציין שהיא מעוניינת לספק שירות cloning (הממשק אינו כולל פונקציה כלשהי). באופן דומה ל-,Java גם ב- #C קיימת תמיכה ב- cloning כבר במחלקת הבסיס,.System.Object אולם כאן יש הבחנה בין העתקה רדודה Copy) (Shalow לבין העתקה עמוקה Copy) :(Deep 1. MemberwiseClone() מבצעת העתקה רדודה, כאשר המצביעים בעצם החדש בעלי ערך זהה לאלו שבעצם המקור. 2. Clone() : אם עצם מסויים מעוניין לאפשר העתקה עמוקה שלו, עליו לממש את הממשק IClonable תוך מימוש הפונקציה Clone() שלו.

Design Patterns 64 Prototype-based Factory בעיה בהמשך ליישום ההודעות מהסעיף הקודם, נדרש להוסיף לתכנית מחלקת dialog לבחירת הודעה חדשה: NewMessageDialog +add_button() +get_selected() +show() +run() «interface» Message Fax -m_number -m_image Mail -m_address -m_text Memo -m_text המחלקה NewMessageDialog מייצגת תיבת dialog לבחירת סוג הודעה חדשה. המחלקה כוללת פונקציה בשם show() לקבלת הבחירה של המשתמש - זה יכול להיות אינדקס או מחרוזת המתארת את טיפוס ההודעה.

פרק : 3 patterns בייצור עצמים 65 תיבת ה- dialog מציגה למשתמש שלושה כפתורים לבחירת סוג הודעה חדשה שברצונו לשלוח: New Message Fax Mail Choose type of message Memo שאלה: כיצד לטפל בבחירת המשתמש ובייצור ההודעה החדשה? פתרון שגוי נבצע switch() על אינדקס ההודעה המוחזר מהפונקציה,show() ועל פי התוצאה נבצע את הקוד המתאים: #include "message.h" class NewMessageDialog... int show(); // display dialog and return selected index Message * run() Message *pm; int i = show(); // get message type index switch(i) case 0: // Fax pm = new Fax; break; case 1: // Mail pm = new Mail; break; ; case 2: return pm; // Memo pm = new Memo; break;

Design Patterns 66 int main() NewMessageDialog dialog; Message * p_new_msg = dialog.run(); קוד משתמש לדוגמא: p_new_msg->print(); // use the new message //... delete p_new_msg; return 0; לפתרון ע"י משפט switch שלושה חסרונות: הוא אינו יעיל - בכל בחירת משתמש יש לעבור על כל האפשרויות, ולבחור את הנכונה. במקרה זה אמנם מעט אפשרויות (3), אך במקרה הכללי, כאשר יש n אפשרויות, בממוצע יבוצע חיפוש על 2/n אפשרויות, כלומר בסיבוכיות.O(n) הוא אינו מודולרי - קוד הלקוח תלוי בטיפוסי ההודעות הקיימים במערכת וצריך להתעדכן בכל שינוי או תוספת של הודעה חדשה. בנוסף, עלולה להיווצר שגיאה בהתאמת האינדקס הנבחר לסוגי ההודעות. הוא אינו דינמי - לא ניתן להוסיף או לבטל אפשרויות נוספות בזמן ריצה.

פרק : 3 patterns בייצור עצמים 67 פתרון הפתרון כולל שני שלבים עקרוניים: 1. שימוש ב- - Prototype נעשה שימוש בפונקציה הוירטואלית clone() שמחזירה העתק של העצם. כמו כן, נוסיף פונקציה וירטואלית נוספת בשם get_type() המחזירה את שם הטיפוס של ההודעה: «interface» Message +get_type() Fax -m_number -m_image +get_tpe() Mail -m_address -m_text +get_type() Memo -m_text +get_type() שתי הפונקציות מוגדרות כוירטואליות טהורות במחלקת הבסיס כך: class Message public: virtual ~Message() virtual Message* clone() const = 0; virtual string get_type() const = 0; virtual void set(const string s1, const string s2) = 0; virtual void print() const = 0; ; כך, למשל, תדרוס המחלקה Mail את הפונקציות: class Mail : public Message public: Mail() Mail(const string addr, const string text) : m_address(addr), m_text(text) virtual Message* clone() const return new Mail(*this); virtual string get_type() const return "Mail"; virtual void set(const string addr, const string text)

Design Patterns 68 m_address=addr; m_text=text; virtual void print() const cout << " * Mail: address=" << m_address << ", text=" << m_text << "\n"; private: string m_address; string m_text; ; 2. נגדיר מחלקה בשם Factory שתחזיק טבלה של - Prototypes מצביעים לעצמי - Message עפ"י מחרוזות המתארות את סוג ההודעה. מחלקה זו תוגדר כ-,Singleton וה- dialog יפנה אליה לייצור עצם עפ"י מחרוזת: NewMessageDialog +add_button() +get_selected() +show() +run() Message +get_type() * 1 m_prototypes Factory -map<string, Message*> m_prototypes +create_object() +add_proto() Fax -m_number -m_image +get_tpe() Mail -m_address -m_text +get_type() Memo -m_text +get_type() קוד המחלקה :Factory #include <map> #include <string> using namespace std; #include "Message.h" // Factory class - Creates Message Objects by their type name. // - Singleton class Factory public: void add(const Message *m) m_prototypes[m->get_type()] = m; Message* create_object(const string &type) return m_prototypes[type]->clone(); static Factory& instance() // singleton access method static Factory factory; return factory; protected:

פרק : 3 patterns בייצור עצמים 69 Factory() Factory(const Factory &); ~Factory() for(map<const string, const Message *>::iterator i= m_prototypes.begin(); i!= m_prototypes.end(); i++) delete i->second; private: map<const string, const Message *> m_prototypes; ; תיבת ה- dialog תציג את ההודעות ובבחירת המשתמש בכפתור מסוים, יבוצע שיבוט (cloning) של העצם המתאים בטבלה: class NewMessageDialog public: void add_button(string name) string show(); Message * run() string sel = show(); return Factory::instance().create_object(sel); ; פונקציה לרישום טיפוסי ההודעות ב- :Factory void register_prototypes() Factory::instance().add_proto("Fax", new Fax); Factory::instance().add_proto("Mail", new Mail); Factory::instance().add_proto("Memo", new Memo); פונקציה זו יכולה להיות גלובלית או במסגרת ה- Facade (להלן, פרק 4), לדוגמא. int main() register_prototypes(); NewMessageDialog dialog; Message * p_new_msg = dialog.run(); // use the new message... קוד תכנית הלקוח: delete p_new_msg; return 0;

Design Patterns 70 כפי שניתן לראות, מבנה מחלקת ה- dialog הוא כעת: יעיל - קוד הפונקציה run() הינו קצר, ויצירת עצם ההודעה החדש מבוצעת בסיבוכיות O(lg.map סיבוכיות חיפוש בטבלה - (n מודולרי - לא קיימת תלות בין פונקצית ההצגה של מחלקת ה- dialog לבין טיפוסי ההודעה. למשל, אין צורך לעדכן פונקציה זו בהוספת סוג הודעה חדש. דינמי - ניתן לקבוע בזמן ריצה אילו טיפוסי הודעה זמינים ע"י ה-,Factory כלומר, אפשר להוסיף ל- Factory עצמי הודעה מטיפוסים חדשים או להסיר עצמים קיימים. כמו כן, מכיוון שהכפתורים מאותחלים עפ"י וקטור ה-,prototypes אין חשש לבילבול בין שמות טיפוסי ההודעה לבין עצמי ההודעה עצמם. הכללה Prototype-based Factory הוא מנגנון לייצור עצמים פולימורפי, תוך הפרדה בין קוד הלקוח לקוד העצם המיוצר: הוא מאפשר לקוד לקוח לייצר עצמים מבלי לציין את הטיפוס המדוייק של העצם, אלא רק מפתח שלו. Client «Prototype» AbstractProduct * 1 m_prototypes Factory +create_object() +add_prototype() ProductA ProductB יתרונות: יעילות: ה- Factory מייצר עצם עפ"י מפתח בסיבוכיות של (n,o(lg כאשר n הוא מספר ה- prototypes האפשרי. מודולריות: הן קוד הלקוח והן ה- Factory עצמו מבודדים מהטיפוסים המדוייקים של העצמים ואינם תלויים בהם. דינמיות : ניתן לקבוע בזמן ריצה אילו טיפוסים יהיו ניתנים לייצור בהקשר הנתון.

פרק : 3 patterns בייצור עצמים 71 יש לשים לב לכך שתוספת טיפוס Prototype אינה מצריכה אף לא הידור מחודש של ה-.Factory המחלקה המשתמשת (בדוגמא הנ"ל, ה- (dialog אינה תלויה אף היא בטיפוסי ה-.Prototypes וריאציות: מפתח ה- prototypes יכול להיות מחרוזת, מזהה מספרי או עצם RTTI של מחלקת ה-.Prototype ב- ++C ניתן להגדיר template של Prototype-based Factory לשימוש כללי. דוגמא: Prototype-based Factory כ- template ב- ++C לטיפוסי מפתח ו- prototype כלליים: #ifndef FACTORY_H #define FACTORY_H #include <map> using namespace std; // Factory class - Creates Objects by a key // - Singleton template <class Key, class Object> class Factory public: void add_proto(const Key key, const Object *proto) m_prototypes[key] = proto; Object* create_object(const Key &key) return m_prototypes[key]->clone(); static Factory& instance() static Factory factory; return factory; // singleton access method ~Factory() for(map<const Key, const Object *>::iterator i= m_prototypes.begin(); i!= m_prototypes.end(); i++) delete i->second; private: map<const Key, const Object *> m_prototypes; Factory() Factory(const Factory &); Factory& operator=(const Factory&); ; #endif